#include "database_list.h"
#include "parser_list.h"

using namespace krryn::parsers;

database_list::database_list(){
	m_EntryName = "entry";
	m_ProtoName = "__proto__";
	m_RootName  = "root";
}

void database_list::load(parser_list_node &a_Root){
	if(!a_Root.m_Properties[m_EntryName] || a_Root.m_Properties[m_EntryName]->empty())
		throw invalid_entry("No '" + m_EntryName + "' found");

	if(m_Reserved.empty()) load_reserved();

	m_Root = &a_Root;

	// The root node is implicitly named 'root'
	m_Root->m_Name = m_RootName;

	m_Entry = get_element_by_name(a_Root.m_Properties[m_EntryName]->front());

	if(!m_Entry){
		throw element_not_found(m_EntryName);
	}else{
		resolve_prototypes(*m_Root);
		build(*m_Entry);
	}
}

parser_list_node *database_list::get_proto_root(parser_list_node *a_Node){
	if(a_Node && a_Node->m_Properties[m_ProtoName]){
		return get_proto_root(
			get_proto_by_name(
			a_Node->m_Properties[m_ProtoName]->front()));
	}

	return a_Node;
}

parser_list_node *database_list::get_proto_by_name(const std::string &a_Name){
	if(m_Reserved[a_Name])
		return m_Reserved[a_Name];
	return get_element_by_name(a_Name);
}

parser_list_node *database_list::get_element_by_name(const std::string &a_Name, parser_list_node *a_Current){
	if(a_Current == 0) a_Current = m_Root;
	if(a_Current->m_Name == a_Name) return a_Current;

	for(std::vector<parser_list_node*>::iterator i = a_Current->m_Children.begin();
		i != a_Current->m_Children.end(); i++){

			parser_list_node *l_Node = get_element_by_name(a_Name, *i);

			if(l_Node) return l_Node;
	}

	return 0;
}

void database_list::resolve_prototypes(parser_list_node &a_Node){
	if(a_Node.m_Properties[m_ProtoName]){

		if(a_Node.m_Properties[m_ProtoName]->size() > 1){
			throw multiple_protos_undefined(a_Node.m_Name);
		}

		parser_list_node *l_Element = get_proto_by_name(
			a_Node.m_Properties[m_ProtoName]->front());

		if(!l_Element){
			throw element_not_found(
				a_Node.m_Properties[m_ProtoName]->front());
		}

		merge_properties(a_Node, *l_Element);
	}

	for(std::vector<parser_list_node*>::iterator i = a_Node.m_Children.begin();
		i != a_Node.m_Children.end(); i++){

			resolve_prototypes(**i);
	}
}

void database_list::merge_properties(parser_list_node &a_Node, parser_list_node &a_Prototype){
	typedef std::map<std::string, std::vector<std::string>* > pt;

	for(pt::iterator j = a_Prototype.m_Properties.begin(); j != a_Prototype.m_Properties.end(); j++){
		if(!a_Node.m_Properties[j->first]){
			a_Node.m_Properties[j->first] = j->second;
		}
	}
}